Delphi code for getting/setting parameter field information

The code below assumes that a PrintJob is already open.

Calls used
  {General}
PEGetNParameterFields
PEGetNthParameterField
  PESetNthParameterField
  {ParameterField To/From ValueInfo}
PEConvertPFInfoToVInfo
  PEConvertVInfoToPFInfo
  {Min/Max}
PEGetParameterMinMaxValue
  PESetParameterMinMaxValue
  {ParameterType}
PEGetNthParameterType
  {ParameterValueInfo}
PEGetParameterValueInfo
  PESetParameterValueInfo
  {DefaultValues}
PEGetNParameterDefaultValues
PEGetNthParameterDefaultValue
  PESetNthParameterDefaultValue
  PEAddParameterDefaultValue
  PEDeleteNthParameterDefaultValue
  {CurrentValues}
PEGetNParameterCurrentValues
PEGetNthParameterCurrentValue
  PEAddParameterCurrentValue
  {Ranges}
  PEClearParameterCurrentValuesAndRanges
PEGetNParameterCurrentRanges
PEGetNthParameterCurrentRange
  PEAddParameterCurrentRange
  {PromptDialog}
  PEGetAllowPromptDialog
  PESetAllowPromptDialog
Code
uses CRDelphi;

procedure GetParameterFieldInformation;
var
  ParameterInfo      : PEParameterFieldInfo;
  ParameterValueInfo : PEParameterValueInfo;
  ValueInfo,  
  minValueInfo, 
  maxValueInfo, 
  startValueInfo,
  endValueInfo       : PEValueInfo;
  nParam,
  nParams,
  nType, 
  iRangeInfo,
  nValues,
  nValue             : smallint;
  sTmp,
  sValueMin,
  sValueMax,
  sRangeStart,
  sRangeEnd          : string;
begin
  ParameterInfo.structSize      := SizeOf(PEParameterFieldInfo);
  ValueInfo.structSize          := SizeOf(PEValueInfo);
  ParameterValueInfo.structSize := SizeOf(PEParameterValueInfo);
  minValueInfo.structSize       := SizeOf(PEValueInfo);
  maxValueInfo.structSize       := SizeOf(PEValueInfo);
  startValueInfo.structSize     := SizeOf(PEValueInfo);
  endValueInfo.structSize       := SizeOf(PEValueInfo);

  {Get the Parameter Names from the Report}
  nParams := 
TPEGetNParameterFields(PEGetNParameterFields)(Cr.FPrintJob);
  if nParams = -1 then
    {Do Error Handler};

  for nParam := 0 to (nParams - 1) do
  begin
    if not PEGetNthParameterField(PrintJob, nParam, ParameterInfo) then
      {Do Error Handler};

    {These are the ParameterInfo members...}
    StrPas(@ParameterInfo.Name);
    StrPas(@ParameterInfo.ReportName);
    StrPas(@ParameterInfo.Prompt);
    ParameterInfo.CurrentValueSet;
    ParameterInfo.needsCurrentValue;
    ParameterInfo.valueType;
    ParameterInfo.isLimited;
    StrPas(ParameterInfo.EditMask);

    {Get DefaultValue into ValueInfo structure}
    ValueInfo.ValueType := ParameterInfo.valueType;
if not PEConvertPFInfoToVInfo(@ParameterInfo.DefaultValue, 
ValueInfo.ValueType, ValueInfo) then
      {Do Error Handler};

    {The ValueInfo structure now holds the DefaultValue}
    {ValueInfoToStr is a sample procedure that converts the DefaultValue
     to a string; you will want to handle this differently}
sTmp := ValueInfoToStr(ValueInfo);

    {Get CurrentValue into ValueInfo structure}
    ValueInfo.ValueType := ParameterInfo.valueType;
if not PEConvertPFInfoToVInfo(@ParameterInfo.CurrentValue, 
ValueInfo.ValueType, ValueInfo) then
      {Do Error Handler};

    {The ValueInfo structure now holds the CurrentValue}
    {ValueInfoToStr is a sample procedure that converts the CurrentValue
     to a string; you will want to handle this differently}
sTmp := ValueInfoToStr(ValueInfo);

    {Min and Max Size}
    if ParameterInfo.isLimited = 1 then
    begin
if not PEGetParameterMinMaxValue(PrintJob, @ParameterInfo.Name, 
@ParameterInfo.ReportName, minValueInfo, maxValueInfo) then
  {Do Error Handler};
      
    case ParameterInfo.valueType of
        PE_PF_NUMBER,
        PE_PF_CURRENCY :
          begin
            {The min/max ValueInfo structures now hold the Min/Max values}
            {ValueInfoToStr is a sample procedure that converts the 
ValueInfo
             value to a string; you will want to handle this differently}
            sValueMin := ValueInfoToStr(minValueInfo);
            sValueMax := ValueInfoToStr(maxValueInfo);
          end;
        PE_PF_STRING   :
          begin
            {The min/max ValueInfo structures now hold the Min/Max values}
            {ValueInfoToStr is a sample procedure that converts the 
ValueInfo
             value to a string; you will want to handle this differently}
            sValueMin := TruncStr(ValueInfoToStr(minValueInfo));
            sValueMax := TruncStr(ValueInfoToStr(maxValueInfo));
          end;
        PE_PF_DATE,
        PE_PF_DATETIME,
        PE_PF_TIME     :
          begin
            {The min/max ValueInfo structures now hold the Min/Max values}
            {ValueInfoToStr is a sample procedure that converts the 
ValueInfo
             value to a string; you will want to handle this differently}
            sValueMin := ValueInfoToStr(minValueInfo);
            sValueMax := ValueInfoToStr(maxValueInfo);
          end;
      end;
    end;

    {Parameter Type}
    nType := PEGetNthParameterType(PrintJob, nParam);
    if nType = -1 then
  {Do Error Handler};

    case nType of
      PE_PO_REPORT     : {SCR Parameter Field}; 
      PE_PO_STOREDPROC : {Stored Procedure Parameter}; 
      PE_PO_QUERY      : {Query Parameter}; 
    end;

    {Get PEParameterValueInfo}
if not PEGetParameterValueInfo(PrintJob, @ParameterInfo.Name, 
@ParameterInfo.ReportName, ParameterValueInfo) then
  {Do Error Handler};

    {These are the ParameterValueInfo members...}
    ParameterValueInfo.isNullable;
    ParameterValueInfo.disallowEditing;
    ParameterValueInfo.allowMultipleValues;
    ParameterValueInfo.hasDiscreteValues;
    ParameterValueInfo.partOfGroup;
    ParameterValueInfo.mutuallyExclusiveGroup;
    ParameterValueInfo.groupNum;
 
    {Get Parameter Field DefaultValues}
    nValues := PEGetNParameterDefaultValues(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.ReportName);
    if nValues = -1 then
      {Do Error Handler};

    for nValue := 0 to (nValues - 1) do
    begin
      if not PEGetNthParameterDefaultValue(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.ReportName, nValue, ValueInfo) then
        {Do Error Handler};
 
      {The ValueInfo structure now holds the DefaultValue}
      {ValueInfoToStr is a sample procedure that converts the 
DefaultValue
       to a string; you will want to handle this differently}
      sTmp := ValueInfoToStr(ValueInfo);
    end;
    
    {Get Parameter Field CurrentValues/Ranges}
    if ParameterValueInfo.hasDiscreteValues = 1 then
    begin
      nValues := PEGetNParameterCurrentValues(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.ReportName);
      if nValues = -1 then
        {Do Error Handler};

      for nValue := 0 to (nValues - 1) do
      begin
        if not PEGetNthParameterCurrentValue(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.ReportName, nValue, ValueInfo) then
          {Do Error Handler};

        {The ValueInfo structure now holds the CurrentValue}
        {ValueInfoToStr is a sample procedure that converts the 
CurrentValue
         to a string; you will want to handle this differently}
        sTmp := ValueInfoToStr(ValueInfo);
      end
      else
      begin
        {Get Parameter Field Ranges}
        nValues := PEGetNParameterCurrentRanges(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.ReportName);
        if nValues = -1 then
          {Do Error Handler};

        {Loop through Ranges}
        for nValue := 0 to (nValues - 1) do
        begin
          iRangeInfo := 0;
          if not PEGetNthParameterCurrentRange(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.ReportName, nValue, startValueInfo, 
endValueInfo, iRangeInfo) then
            {Do Error Handler};

          {The startValueInfo structure now holds the Range Start Value}
          {ValueInfoToStr is a sample procedure that converts the 
startValueInfo
           value to a string; you will want to handle this differently}
          sRangeStart := ValueInfoToStr(startValueInfo);
          {The ValueInfo structure now holds the Range End Value}
          {ValueInfoToStr is a sample procedure that converts the 
endValueInfo
           value to a string; you will want to handle this differently}
          sRangeEnd := ValueInfoToStr(endValueInfo);

          {Get Range Bounds info}
          if (iRangeInfo - PE_RI_NOLOWERBOUND > -1) then
          begin
            iRangeInfo := iRangeInfo - PE_RI_NOLOWERBOUND;
            if iRangeInfo - PE_RI_NOUPPERBOUND > -1 then
              {Upper and Lower Bounds not included in range}
            else
              {Upper Bound only included in range};
          end
          else
          begin
            if iRangeInfo - PE_RI_INCLUDELOWERBOUND > -1 then
            begin
              iRangeInfo := iRangeInfo - PE_RI_INCLUDELOWERBOUND;
              if iRangeInfo - PE_RI_INCLUDEUPPERBOUND > -1 then
                {Upper and Lower Bounds included in range}
              else
                {Lower Bound only included in range};
            end;
          end;
        end;
      end;
    end;
  end;
end;


{Returns the ValueInfo value as a formatted string}
function ValueInfoToStr (var ValueInfo: PEValueInfo): string;
var
  sTmp : string;
begin
  sTmp := '';
  case ValueInfo.ValueType of
    {Number}
    PE_PF_NUMBER : Str(ValueInfo.viNumber:0:2, sTmp);
    {Currency}
    PE_PF_CURRENCY : Str(ValueInfo.viCurrency:0:2, sTmp);
    {Boolean}
    PE_PF_BOOLEAN : sTmp := BooleanToStr(ValueInfo.viBoolean, False);
    {Date}
    PE_PF_DATE :  {YYYY,MM,DD}
      begin
        {Crystal's null datetime is -4713,1,1 0:0:0; Delphi does not
         allow for negative dates in it's TDateTime, so I use 1,1,1 
instead} 
        if ValueInfo.viDate[0] < 1 then
          sTmp := '1,1,1'
        else
        begin
          sTmp := IntToStr(ValueInfo.viDate[0]) + ',' +
                  IntToStr(ValueInfo.viDate[1]) + ',' +
                  IntToStr(ValueInfo.viDate[2]);
        end;
      end;
    {String}
    PE_PF_STRING : sTmp := StrPas(ValueInfo.viString);
    {DateTime}
    PE_PF_DATETIME :  {YYYY,MM,DD HH:MM:SS}
      begin
        {Crystal's null datetime is -4713,1,1 0:0:0; Delphi does not
         allow for negative dates in it's TDateTime, so I use 1,1,1 
instead} 
        if ValueInfo.viDateTime[0] < 1 then
          sTmp := '1,1,1 1:1:1'
        else
        begin
          sTmp := IntToStr(ValueInfo.viDateTime[0]) + ',' +
                  IntToStr(ValueInfo.viDateTime[1]) + ',' +
                  IntToStr(ValueInfo.viDateTime[2]) + ' ' +
                  IntToStr(ValueInfo.viDateTime[3]) + ':' +
                  IntToStr(ValueInfo.viDateTime[4]) + ':' +
                  IntToStr(ValueInfo.viDateTime[5]);
        end;
      end;
    {Time}
    PE_PF_TIME :  {HH:MM:SS}
      begin
        {Crystal's null datetime is -4713,1,1 0:0:0; Delphi does not
         allow for zero times in it's TDateTime, so I use 1:1:1 instead} 
        if ValueInfo.viTime[0] < 1 then
          sTmp := '1:1:1'
        else
        begin
          sTmp := IntToStr(ValueInfo.viTime[0]) + ':' +
                  IntToStr(ValueInfo.viTime[1]) + ':' +
                  IntToStr(ValueInfo.viTime[2]);
        end;
      end;
  end;
  Result := sTmp;
end;


{Removes the floating point from a numeric string}
function TruncStr (sValue: string): string;
var
  i : integer;
begin
  Result := '';
  i := Pos('.', sValue);
  if i > 0 then
    Result := Copy(sValue, 1, i-1);
end;


procedure SetParameterFields;
var
  ParameterInfo           : PEParameterFieldInfo;
  ParameterValueInfo      : PEParameterValueInfo;
  ValueInfo               : PEValueInfo;
  minValueInfo            : PEValueInfo;
  maxValueInfo            : PEValueInfo;
  startValueInfo          : PEValueInfo;
  endValueInfo            : PEValueInfo;
  nParams,
  nParam,
  nValues,
  nValue                  : smallint;
  iRangeInfo              : smallint;
  bShowDialog             : Bool;
begin
  ParameterInfo.structSize := SizeOf(PEParameterFieldInfo);
  ParameterValueInfo.structSize := SizeOf(PEParameterValueInfo);
  ValueInfo.structSize := SizeOf(PEValueInfo);
  minValueInfo.structSize := SizeOf(PEValueInfo);
  maxValueInfo.structSize := SizeOf(PEValueInfo);
  startValueInfo.structSize := SizeOf(PEValueInfo);
  endValueInfo.structSize := SizeOf(PEValueInfo);

  {Get the number of Parameter Fields}
  nParams := PEGetNParameterFields(PrintJob);
  if nParams = -1 then
    {Do Error Handler};

  {Turn off the Prompt Dialog: only turn on if needed}
  bShowDialog := PEGetAllowPromptDialog(PrintJob);
  if bShowDialog <> False then
  begin
    bShowDialog := Bool(False);
    if not PESetAllowPromptDialog(PrintJob, bShowDialog) then
      {Do Error Handler};
  end; 

  {Loop through the Parameter Fields}
  for nParam := 0 to (nParams - 1) do
  begin
    if not PEGetNthParameterField(PrintJob, nParam, ParameterInfo) then
      {Do Error Handler};

    {Here we simulate passing a new value into the Parameter}
    {You would normally know in advance what type of Parameter you 
     are dealing with, but we cover every type here...}
    ValueInfo.valueType := ParameterInfo.valueType;
    case ParameterInfo.valueType of
      {Number}
      PE_PF_NUMBER : ValueInfo.viNumber := 22;
      {Currency}
      PE_PF_CURRENCY : ValueInfo.viCurrency := 2.22;
      {Boolean}
      PE_PF_BOOLEAN : ValueInfo.viBoolean := True;
      {Date}
      PE_PF_DATE :  {YYYY,MM,DD}
        begin
          ValueInfo.viDate[0] := 1998;
          ValueInfo.viDate[1] := 10;
          ValueInfo.viDate[2] := 30;
        end;
      {String}
      PE_PF_STRING : StrPCopy(@ValueInfo.viString, 'New Value');
      {DateTime}
      PE_PF_DATETIME :  {YYYY,MM,DD HH:MM:SS}
        begin
          ValueInfo.viDateTime[0] := 1998;
          ValueInfo.viDateTime[1] := 10;
          ValueInfo.viDateTime[2] := 30;
          ValueInfo.viDateTime[3] := 12;
          ValueInfo.viDateTime[4] := 22;
          ValueInfo.viDateTime[5] := 32;
        end;
      {Time}
      PE_PF_TIME :  {HH:MM:SS}
        begin
          ValueInfo.viTime[0] := 12;
          ValueInfo.viTime[1] := 22;
          ValueInfo.viTime[2] := 32;
        end;
    end;

    {Parameter Prompt Value}
    StrPCopy(@ParameterInfo.Prompt, 'Enter a Value: ');

    {CurrentValueSet}
    {0 = Show Prompt Dialog, 1 = bypass Prompt Dialog}
    ParameterInfo.CurrentValueSet := 1;

    {DefaultValueSet}
    {1 = Show Prompt Dialog, 0 = bypass Prompt Dialog}
    ParameterInfo.DefaultValueSet := 0;

    case ParameterInfo.CurrentValueSet of
      0: {Default Value: change the prompt, but still prompt}
         begin
           {Convert ValueInfo to ParamFieldInfo DefaultValue}
           if not PEConvertVInfoToPFInfo(ValueInfo, ValueInfo.ValueType, 
@ParameterInfo.DefaultValue) then
             {Do Error Handler};

           {Turn on PromptDialog}   
           bShowDialog := Bool(True);
           if not PESetAllowPromptDialog(PrintJob, bShowDialog) then
             {Do Error Handler};
         end;

      1: {Current Value: pass in a new value, no parameter prompt}
         begin
           {Convert ValueInfo to ParamFieldInfo CurrentValue}
           if not PEConvertVInfoToPFInfo(ValueInfo, ValueInfo.ValueType, 
@ParameterInfo.CurrentValue) then
             {Do Error Handler};
         end;
    end;

    {EditMask: Do not use EditMask and Min/Max limits at the same time, 
they are mutually exclusive}
    StrPCopy(@ParameterInfo.EditMask, '');

    {isLimited: 1 is On, 0 is Off}
    ParameterInfo.isLimited := 1;

    {Should only set Min/Max if isLimited = 1}
    {Number, Currency, & String can be passed via PESetNthParameterField, 
but Date, Time, and DateTime require PESetParameterMinMaxValue}
    if ParameterInfo.isLimited = 1 then
    begin
      minValueInfo.ValueType := ParameterInfo.valueType;
      maxValueInfo.ValueType := ParameterInfo.valueType;
      {Min/Max}
      case ParameterInfo.valueType of
        PE_PF_NUMBER :
          begin
            ParameterInfo.MinSize := 12;
            ParameterInfo.MaxSize := 25;
           end;
        PE_PF_CURRENCY :
          begin
            ParameterInfo.MinSize := 1.2;
            ParameterInfo.MaxSize := 2.5;
           end;
        PE_PF_STRING :
           begin
             ParameterInfo.MinSize := 5;
             ParameterInfo.MaxSize := 10;
           end;
        PE_PF_DATE :  {YYYY,MM,DD}
           begin
             minValueInfo.viDate[0] := 1997;
             minValueInfo.viDate[1] := 10;
             minValueInfo.viDate[2] := 30;
             maxValueInfo.viDate[0] := 1998;
             maxValueInfo.viDate[1] := 10;
             maxValueInfo.viDate[2] := 30;
             if not PESetParameterMinMaxValue(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.reportName, minValueInfo, 
maxValueInfo) then
           end;
        {DateTime}
        PE_PF_DATETIME :  {YYYY,MM,DD HH:MM:SS}
           begin
             minValueInfo.viDateTime[0] := 1997;
             minValueInfo.viDateTime[1] := 10;
             minValueInfo.viDateTime[2] := 30;
             minValueInfo.viDateTime[3] := 12;
             minValueInfo.viDateTime[4] := 22;
             minValueInfo.viDateTime[5] := 32;
             maxValueInfo.viDateTime[0] := 1998;
             maxValueInfo.viDateTime[1] := 10;
             maxValueInfo.viDateTime[2] := 30;
             maxValueInfo.viDateTime[3] := 12;
             maxValueInfo.viDateTime[4] := 22;
             maxValueInfo.viDateTime[5] := 32;
             if not PESetParameterMinMaxValue(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.reportName, minValueInfo, 
maxValueInfo) then
           end;
        {Time}
        PE_PF_TIME :  {HH:MM:SS}
           begin
             minValueInfo.viTime[0] := 01;
             minValueInfo.viTime[1] := 22;
             minValueInfo.viTime[2] := 32;
             maxValueInfo.viTime[0] := 12;
             maxValueInfo.viTime[1] := 22;
             maxValueInfo.viTime[2] := 32;
             if not PESetParameterMinMaxValue(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.reportName, minValueInfo, 
maxValueInfo) then
           end;
      end;
    end;

    {Set the Parameter field}
    if not PESetNthParameterField(PrintJob, nParam, ParameterInfo) then
      {Do Error Handler};
    
    {Send Parameter Field Info}
    ParameterValueInfo.isNullable := 0;
    ParameterValueInfo.disallowEditing := 0;
    ParameterValueInfo.allowMultipleValues := 1; {True}
    ParameterValueInfo.hasDiscreteValues := 0;   {has Ranges}
    ParameterValueInfo.partOfGroup := 0;
    ParameterValueInfo.mutuallyExclusiveGroup := 0;
    ParameterValueInfo.groupNum := 0;
    if not PESetParameterValueInfo(PrintJob, @ParameterInfo.Name, 
@ParameterInfo.ReportName, ParameterValueInfo) then
      {Do Error Handler};

    {Send DefaultValues: only required if the PromptDialog is going to 
show}
    {You must use the ValueInfo structure to send in or change values.  
See code above on how to do this}
    ValueInfo.ValueType := ParameterInfo.valueType;
    {Get Number of DefaultValues}
    nValues := PEGetNParameterDefaultValues(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.ReportName);
    if nValues = -1 then
      {Do Error Handler};

    {Loop through DefaultValues}
    for nValue := (nValues - 1) downto 0 do
    begin
      if not PEGetNthParameterDefaultValue(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.ReportName, nValue, ValueInfo) then
        {Do Error Handler};
      {If you want to change a DefaultValue, use PESetNth...}  
      if not PESetNthParameterDefaultValue(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.ReportName, nValue, ValueInfo) then
        {Do Error Handler};
      {If you want to delete a DefaultValue, use PEDeleteNth...}  
      if not PEDeleteNthParameterDefaultValue(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.ReportName, nValue) then
        {Do Error Handler};
    end;
    {If you want to add a DefaultValue, use PEAdd...}  
    if not PEAddParameterDefaultValue(PrintJob, @ParameterInfo.Name), 
@ParameterInfo.ReportName), ValueInfo) then
        {Do Error Handler};
 
    {Parameter Field CurrentValues/Ranges, only required if the Prompt 
Dialog is not going to show but will be bypassed}
    if ParameterInfo.SetCurrentValue = 1 then
    begin
      {Parameter Field CurrentValues}
      if ParameterValueInfo.hasDiscreteValues = 1 then
      begin
        {If you want to change CurrentValues, you must first clear the 
CurrentValues in the Report, then Add the values you want}
        if not PEClearParameterCurrentValuesAndRanges(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.ReportName) then
          {Do Error Handler};

        {Add CurrentValue}
        if not PEAddParameterCurrentValue(PrintJob, @ParameterInfo.Name, 
@ParameterInfo.ReportName, ValueInfo) then
          {Do Error Handler};
      end
      {Parameter Field Ranges}
      else
      begin
        {If you want to change Ranges, you must first clear the Ranges in 
the Report, then Add the values you want}
        if not PEClearParameterCurrentValuesAndRanges(PrintJob, 
@ParameterInfo.Name, @ParameterInfo.ReportName) then
          {Do Error Handler};

        {You must use the ValueInfo structure to change ranges.  See code 
above on how to do this}
        startValueInfo.valueType := ParameterInfo.valueType;
        endValueInfo.valueType := ParameterInfo.valueType;

        {RangeInfo can be one of the following options.  RangeInfo 
determines if the Parameter Range available (for selecting records in the 
Report) will include the start and end Range values or not}
        iRangeInfo := PE_RI_INCLUDEUPPERBOUND and 
PE_RI_INCLUDELOWERBOUND;
        iRangeInfo := PE_RI_INCLUDELOWERBOUND;
        iRangeInfo := PE_RI_INCLUDEUPPERBOUND;
        iRangeInfo := PE_RI_NOUPPERBOUND and PE_RI_NOLOWERBOUND;

        {Add New Range settings}
        if not PEAddParameterCurrentRange(PrintJob, @ParameterInfo.Name, 
@ParameterInfo.ReportName, startValueInfo, endValueInfo, iRangeInfo) 
then
          {Do Error Handler};
      end;
    end; 
  end; 
end;


Seagate Software IMG Holdings, Inc.
http://www.seagatesoftware.com
Support services:
http://support.seagatesoftware.com